/* pcode.c
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "pcode.h"
#include "error.h"

int FunctionDepth = 0;


/** */
SYMTREE buildVarNode(ASTTREE tree){
	SYMTREE varNode = (SYMTREE) createSymTree();
	varNode->bval = tree->right->bval;
	varNode->sval = tree->right->sval;
	varNode->ival = tree->right->ival;
	varNode->nodeType = AT_DECLARE;
	varNode->typeVal = tree->vtype;
	varNode->next = NULL;
	return varNode;
}

/** */
SYMTREE buildArg(ASTTREE tree){
	TraceMsg2("buildArg","entry",humanReadableNodeType(tree->type));
	if(tree == NULL) return NULL;
	if(tree->type != AT_ARGS)return NULL;
	TraceMsg("buildArg","null check");
	SYMTREE argNode = (SYMTREE) createSymTree();
	argNode->sval = tree->left->sval;
	argNode->nodeType = AT_ARGS;
	argNode->typeVal = tree->vtype;
	SYMTREE nextArg = NULL;
	TraceMsg("buildArg","builded");
	if(tree->right!=NULL)
		nextArg = buildArg(tree->right);
	TraceMsg("buildArg","next");
	argNode->next = nextArg;

	return argNode;

}


/** */
SYMTREE buildFuncNode(ASTTREE tree){
	SYMTREE varNode = (SYMTREE) createSymTree();
	varNode->sval = tree->left->right->sval;
	varNode->nodeType = AT_FUNC;
	varNode->typeVal = tree->left->vtype;
	varNode->next = NULL;
	varNode->isForward = ((tree->right==NULL)?1:0);
	if(tree->left->left != NULL){
		TraceMsg("buildFuncNode",humanReadableNodeType(tree->left->left->type));
		varNode->args = buildArg(tree->left->left);
	}
	return varNode;
}


int fillSymbolTree(ASTTREE tree, SYMTREE symTree){
	if (tree == NULL)
		return 0;

	if (tree->type == AT_PROG) {
		if (tree->right->type == AT_FUNC) {
			SYMTREE func = buildFuncNode(tree->right);
			int ret = add(symTree, func,0);
			if (ret != 0)
				return ret;
			printSymTree(symTree);
			return fillSymbolTree(tree->right->right, symTree);
		}
	}
	if (tree->type == AT_DECLARE) /* Add the declared var eg: var x int; */
	{
		int type = tree->vtype;
		if (tree->right->type == AT_ID && tree->right->sval != NULL) {
			TraceMsg("Trying to add new var",tree->right->sval);
			SYMTREE var = searchVarAtCurrentLevel(symTree, tree->right->sval);
			if (var != NULL){
				return Error(1955, "The variable is already defined.");
			}
			SYMTREE varNode = buildVarNode(tree);
			int ret = add(symTree, varNode,0);
			if (ret != 0)
				return ret;
			return fillSymbolTree(tree->left, symTree);
		}
		if (tree->right->type == AT_FUNC
				&& tree->right->left->right->sval != NULL) {
			SYMTREE currentSymbole = searchFunctionDefinition(symTree, tree->right);
			if (currentSymbole == NULL || currentSymbole->isForward) {
				SYMTREE func = buildFuncNode(tree->right);
				TraceMsg2("fillSymbolTree","parent",symTree->sval);
				TraceMsg2("fillSymbolTree","child",func->sval);
				int ret = add(symTree, func,1);
				if (ret != 0)
					return ret;
				ret = fillSymbolTree(tree->right, func);
				if ( ret != 0)
					return ret;
				return fillSymbolTree(tree->left, symTree);
			} else{
				return Error(1322, "The current function already defined");
			}
		}
	}

	if (fillSymbolTree(tree->left, symTree) == -1
			|| fillSymbolTree(tree->right, symTree) == -1)
		return -1;

	return 0;

}


int pcodeGenAddress(ASTTREE tree, SYMTREE s) {
 
	int location;

	if (tree == NULL)
		return 0;

	switch (tree->type) {

	case AT_ID: // variable
		//location = getLocation(s, tree->sval);
		//location=searchForVarOrArg(s, tree->sval, int type)
		location=LocationSearch(s,tree);
		if (location < 0) {
			// this should NOT happen, since it will cause havoc on adress space
			fprintf(stderr, "KO\n");
			printf("ERROR : (!!) pcodeGenAddress : VAR '%s' has no location\n",
					tree->sval);
			exit(1);
		} else {
			printf("; loading ADRESS of variable '%s'\n", tree->sval);

			if (tree->sval != NULL) {
				//int myType = varTypeSearch(s, tree->sval);
					int myType =TypeSearch(s,tree);
				printf(";myType %d\n", myType);

				if (myType == AT_NB || myType == AT_NEG)
					printf("lda i %d %d\n",FunctionDepth-1, location); // add depth difference calculation
				else if (myType == AT_BOOL)
					printf("lda b %d %d\n",FunctionDepth-1, location);
				else
					printf("lda a %d %d\n",FunctionDepth-1, location);
			}

		}
		break;

	default:
		fprintf(stderr, "KO\n");
		printf("ERROR : unrecognized type=%d in pcodeGenAddress(..)\n",
				tree->type);
		exit(1);
	}

	return 0;
}

int pcodeGenValue(ASTTREE tree, SYMTREE s) {
	 
	int location;
	static int staticlabel = 0;
	int label = staticlabel;

	staticlabel++;

	if (tree == NULL)
		return 0;

	switch (tree->type) {
	case AT_PROG:

		printf("define @%s\n", tree->left->sval);
		//printf("ssp %d\n", getMaxMemoryUsage(s) );

		printf("ujp @main\n");

		pcodeGenValue(tree->right, s);

		printf("define @end\n");
		printf("; end of program\n");
		printf("stp\n");
		break;

	case AT_ID: // variable
		printf("; AT_ID: %s\n", tree->sval);

//		location = getLocation(s, tree->sval);
		location=LocationSearch(s,tree);
		if (location < 0) {
			// this should NOT happen, since it will cause havoc on address space
			fprintf(stderr, "KO\n");
			printf("ERROR : (!!) pcodeGenValue : VAR '%s' has no location\n",
					tree->sval);
			exit(1);
		} else {
			if (tree->sval != NULL) {
				printf("; loading value of variable '%s'\n", tree->sval);
				//search for ID type in symbol table ; evaluate the result and printf "i" or "b"
				//int myType = varTypeSearch(s, tree->sval);
					int myType =TypeSearch(s,tree);
				printf(";AT_ID:myType %d\n", myType);

				if (myType == AT_NB || myType == AT_NEG)
					printf("lda i %d  %d\nind i\n",FunctionDepth-1, location);
				else if (myType == AT_BOOL)
					printf("lda b %d %d\nind b\n",FunctionDepth-1, location);
				//else printf("lda a 0 %d\n",location);
			}
		}
		break;

	case AT_OPASS:

		printf("; begin of affectation %d\n", label);
		pcodeGenAddress(tree->left, s);
		pcodeGenValue(tree->right, s);

		printf("; OPASS:type: %s\n", humanReadableNodeType(tree->right->type));
		if (tree->right->type == AT_BOOL) { // store boolean
			printf("sto b\n");
		} else if (tree->right->type == AT_NB || tree->right->type == AT_NEG) { // store integer
			printf("sto i\n");
		} else if (tree->right->type == AT_ID) {
			if (tree->right->sval != NULL) {
				//AT_ID
				//search for ID type in symbol table ; evaluate the result and printf "i" or "b"
				//int myType = varTypeSearch(s, tree->right->sval);
					int myType =TypeSearch(s,tree->right);
				printf(";OPASS:myType %d\n", myType);
				if (myType == AT_NB || myType == AT_NEG)
					printf("sto i\n");
				else if (myType == AT_BOOL)
					printf("sto b\n");
				else
					printf("sto a\n");
			}
		} else if (tree->right->type == AT_OPADD
				|| tree->right->type == AT_OPSUB
				|| tree->right->type == AT_OPDIV
				|| tree->right->type == AT_OPMUL) { // store calcul result
			printf("sto i\n");
		} else if (tree->right->type == AT_AND || tree->right->type == AT_NOT
				|| tree->right->type == AT_OR || tree->right->type == AT_LE
				|| tree->right->type == AT_LT || tree->right->type == AT_OPEQ) { // store boolean result
			printf("sto b\n");
		} else if (tree->right->type == AT_FUNC_CALL) { // store function result
			printf(";AT_OPASS:tree->right->left->sval: %s\n",tree->right->left->sval);
			if (tree->right->left->sval != NULL) {
					int myType =TypeSearch(s,tree->right->left);
			//	if (funcTypeSearch(s, tree->right->left ) == AT_NB)
				if (myType == AT_NB)
					{ 
						  
			  		printf("sto i\n");
				  }
				//else if (funcTypeSearch(s, tree->right->left ) == AT_BOOL)
					else	if (myType == AT_BOOL)
			  {			   
			   	printf("sto b\n");
				}
			}
		}
		printf("; end of affectation %d\n", label);
		break;

	case AT_NB: // raw number
		printf("; loading constant value '%d'\n", tree->ival);
		printf("ldc i %d\n", tree->ival);
		break;

	case AT_OPADD: // sum
		printf("; begin of sum %d\n", label);
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("add i\n");
		printf("; end of sum %d\n", label);
		break;

	case AT_OPSUB: // substraction
		printf("; begin of substraction %d\n", label);
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("sub i\n");
		printf("; end of substraction %d\n", label);
		break;

	case AT_OPMUL: // multiplication
		printf("; begin of multiplication %d\n", label);
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("mul i\n");
		printf("; end of the multiplication %d\n", label);
		break;

	case AT_OPDIV: // division
		printf("; begin of division %d\n", label);
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("div i\n");
		printf("; end of the division %d\n", label);
		break;

	case AT_OPWRITE: // print command - top of stack must be INT
		printf("; begin of print %d\n", label);
		pcodeGenValue(tree->left, s);
		printf("prin\n");
		printf("; end of print %d\n", label);
		break;

	case AT_OPREAD: // read command - read value must be INT
		printf("; begin of read %d\n", label);

		if (tree->left->type == AT_ID)
			pcodeGenAddress(tree->left, s);
		else
			pcodeGenValue(tree->left, s);

		printf("read\nsto i\n");
		printf("; end of read %d\n", label);
		break;

	case AT_OPEQ:
		printf(";AT_OPEQ\n");

		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);

		printf("equ i\n");

		break;
	case AT_LT:
		printf(";AT_LT\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);

		printf("les i\n");

		break;

	case AT_LE:
		printf(";AT_LE\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("leq i\n");
		break;
	case AT_OR:
		printf(";AT_OR\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("or b\n");
		break;
	case AT_AND:
		printf(";AT_AND\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("and b\n");
		break;

	case AT_ILIST:
		//printf(";AT_ILIST\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		break;

	case AT_BOOL:
		printf("; loading constant value '%d'\n", tree->ival);
		printf("ldc b %d\n", tree->bval);
		break;

	case AT_NOT:
		printf(";AT_NOT\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("not b\n");
		break;

	case AT_DECLARE:
		printf(";AT_DECLARE\n");

		if (tree->right != NULL && tree->right->type == AT_FUNC) {
			printf(";AT_DECLARE->AT_FUNC\n");
			pcodeGenValue(tree->right, s); // provoke KO in L2
			pcodeGenValue(tree->left, s); // provoke KO in L2

		} else {
			pcodeGenValue(tree->left, s);
		}

		break;

	case AT_DECLARE_BLOC:
		printf(";AT_DECLARE_BLOC\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		break;

	case AT_FUNC:
		printf(";AT_FUNC\n");
		FunctionDepth++;
		pcodeGenValue(tree->left, s); // AT_FUNC_HEAD
		pcodeGenValue(tree->right, s); // AT_FUNC_BODY
		FunctionDepth--;
		// if(FunctionDepth==0)  pcodeGenValue(tree->left,s);  // AT_FUNC_HEAD

		break;

	case AT_FUNC_HEAD:
		printf(";AT_FUNC_HEAD\n");
		//if( strcmp(tree->right->sval,"main") !=0 ) printf("define @%s\n",tree->right->sval);  // skip define @main
		// if( FunctionDepth >1 )printf("define @%s\n",tree->right->sval);
		printf("define @%s\n", tree->right->sval);
		//  printf("lda i 0 0\n");
		break;

	case AT_FUNC_BODY:
		printf(";AT_FUNC_BODY\n");
		if (tree->right != NULL) {
			pcodeGenValue(tree->right, s);
		}
		if (FunctionDepth > 1) { //not in main
								 //if(tree->right==NULL || tree->right->right==NULL || tree->right->right->type!=AT_RETURN)
			printf("retp\n");
		} else
			printf("ujp @end\n"); //in main
		if (tree->left != NULL) {
			pcodeGenValue(tree->left, s);
		}
		/*
		 pcodeGenValue(tree->left,s);
		 if(FunctionDepth==1) printf("define @main\n");

		 pcodeGenValue(tree->right,s);

		 if(FunctionDepth>1) printf("retp\n");
		 */

		break;

	case AT_IF:

		printf(";AT_IF\n");
		//printf("define @IF%p\n",tree);
		pcodeGenValue(tree->left, s); //evaluation of the IF condition: write TRUE or FALSE on top of stack
		pcodeGenValue(tree->right, s); //execute the AT_IF_BODY code

		break;

	case AT_IF_BODY:

		printf(";AT_IF_BODY\n");
		printf("fjp @IFFALSE%p\n", tree); //evaluate the top of stack: if false: goto IFFALSE else continue

		printf("define @IFTRUE%p\n", tree); // IF==TRUE
		pcodeGenValue(tree->left, s); // IF==TRUE ACTION
		printf("ujp @FI%p\n", tree); // GOTO FI
		printf("define @IFFALSE%p\n", tree); //IF==FALSE
		pcodeGenValue(tree->right, s); // IF==FALSE ACTION

		printf("define @FI%p\n", tree);
		break;

	case AT_WHILE:

		printf(";AT_WHILE %p\n", tree);
		printf("define @WHILEDO%p\n", tree);
		pcodeGenValue(tree->left, s); //evaluation of the condition: write TRUE or FALSE on top of stack
		printf("fjp @WHILEOD%p\n", tree);
		pcodeGenValue(tree->right, s); //execute the   code
		printf("ujp @WHILEDO%p\n", tree);
		printf("define @WHILEOD%p\n", tree);

		break;

	case AT_ARGS:
		printf(";AT_ARGS\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		break;

	case AT_UNKNOWN:
		printf(";AT_UNKNOWN\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);

		break;
	case AT_ISET:
		printf(";AT_ISET\n");
		pcodeGenValue(tree->right, s);
		pcodeGenValue(tree->left, s);
		break;
	case AT_VOID:
		printf(";AT_VOID\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		break;
	case AT_NEG:
		printf(";AT_NEG\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		printf("neg i\n");
		break;
case AT_RETURN:
		printf(";AT_RETURN scope:%d \n", FunctionDepth);

		    //pcodeGenValue(tree->right,s); //NULL
		    //  pcodeGenValue(tree->left,s);  //AT_ID
			 //if (tree->left->sval != NULL)  printf(";AT_RETURN  tree->left->sval: %d\n",funcTypeSearch(s, tree->left->sval));
			 if (tree->left->sval != NULL)  printf(";AT_RETURN  tree->left->sval: %d\n",TypeSearch(s, tree->left));
		     if(tree->left != NULL)
			 {
		         	printf(";	tree->left->type: %d\n",	tree->left->type );
						//if (tree->left->sval != NULL )  	printf(";getType:%d\n",funcTypeSearch(s, tree->left->sval) );
						if (tree->left->sval != NULL )  	printf(";getType:%d\n",TypeSearch(s, tree->left) );
		   	
						if (tree->left->sval != NULL && tree->left->type == AT_ID) 
						{ //return a variable
							//int myType=varTypeSearch(s, tree->left->sval);
						int myType=	TypeSearch(s, tree->left);
							if ( myType == AT_NB  ) 
								{
									printf("lda i %d 0\n",0);
									pcodeGenValue(tree->left, s);
									printf("sto i\n");
									printf("retf\n");
						  	} 
							 else if (myType == AT_BOOL)
								 {
									printf("lda b %d 0\n",0);
									pcodeGenValue(tree->left, s);
									printf("sto b\n");
									printf("retf\n");
							   }			
						}
						else if ( tree->left->type == AT_BOOL )
						{
 							printf("lda b %d 0\nldc b %d\nsto b\nretf\n",0, tree->left->bval); //return boolean value // replace ldc b %d by pcodegenvalue?
						}
						else if (tree->left->type == AT_NB ||  tree->left->type == AT_NEG )
						{
 							printf("lda i %d 0\nldc i %d\nsto i\nretf\n",0, tree->left->ival); //return integer value
						}
						else if ( tree->left->type == AT_AND || tree->left->type == AT_NOT || tree->left->type == AT_OR || tree->left->type == AT_LE || tree->left->type == AT_LT || tree->left->type == AT_OPEQ )
						{
							printf("lda b %d 0\n",0);
							pcodeGenValue(tree->left,s); 
							printf("sto b\nretf\n");
						}
						else if ( tree->left->type == AT_OPADD ||  tree->left->type == AT_OPSUB ||  tree->left->type == AT_OPDIV ||  tree->left->type == AT_OPMUL)
						{
							printf("lda i %d 0\n",0);
							pcodeGenValue(tree->left,s); 
							printf("sto i\nretf\n");
						}
						else printf(";unknown return type !");

			 }
						else printf(";null return type !");
		
			break;
	case AT_FUNC_CALL:
		printf(";AT_FUNC_CALL\n");

		printf("mst %d\n",FunctionDepth-1);
		printf("cup 0 @%s\n", tree->left->sval); // call function with 0 parameters
  	//LEFT=AT_ID
		//RIGHT=NULL
		//  if(tree->left->sval!=NULL)//to move to at_opass ?
		{
			//   if( varTypeSearch(s, tree->left->svalh)==AT_NB) printf("sto i\n");
			//   else if( varTypeSearch(s, tree->left->sval)==AT_BOOL) printf("sto b\n");
		}

		break;
	case AT_DEL_FROM:
		printf(";AT_DEL_FROM\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);
		break;
	case AT_ADD_TO:
		printf(";AT_ADD_TO\n");
		pcodeGenValue(tree->left, s);
		pcodeGenValue(tree->right, s);

		break;
	case AT_SHARP:
		printf(";AT_SHARP\n");
		pcodeGenValue(tree->right, s);
		pcodeGenValue(tree->left, s);

		break;
	case AT_IN:
		printf(";AT_IN\n");
		pcodeGenValue(tree->right, s);
		pcodeGenValue(tree->left, s);
		break;

	case AT_IMPL:
		printf(";AT_IMPL\n");
		// printf("ssp 2\n"); // UPDATE SSP for variable reservation in function
		//printf("ssp %d\n", getMaxMemoryUsage(s,FunctionDepth) );
		printf("ssp %d\n", getMaxMemoryUsage(s));
		pcodeGenValue(tree->right, s);
		pcodeGenValue(tree->left, s);
		break;

	default:
		fprintf(stderr, "KO\n");
		printf("ERROR : unrecognized type=%d in pcodeGenValue(..)\n",
				tree->type);
	}

	return 0;
}

